#include "net/TcpConnection.h"
#include "net/EventLoop.h"
#include "Base/Logger.h"
#include "Base/CurrectThread.h"
#include <cassert>
#include <errno.h>

namespace toym
{
namespace net
{

TcpConnection::TcpConnection(int fd, EventLoop* loop, const InetAddress& localAddr, const InetAddress& peerAddr)
    : m_fd(fd),
      m_sock(fd),
      m_loop(loop),
      m_localAddr(localAddr),
      m_peerAddr(peerAddr),
      m_channel(fd, loop),
      m_connectionCb(),
      m_messageCb(),
      m_closeCb(),
      m_writeComplateCb(),
      m_highWaterMarkCb(),
      m_readBuf(),
      m_writeBuf(),
      m_state(State::eConnecting),
      m_connName()
{
    m_channel.setReadCallback(std::bind(&TcpConnection::handleRead, this));
    m_channel.setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
    m_channel.setCloseCallback(std::bind(&TcpConnection::handleClose, this));
}

TcpConnection::TcpConnection(Socket sock, EventLoop* loop, const InetAddress& localAddr, const InetAddress& peerAddr)
    : m_fd(sock.getFd()),
      m_sock(std::move(sock)),
      m_loop(loop),
      m_localAddr(localAddr),
      m_peerAddr(peerAddr),
      m_channel(m_fd, loop),
      m_connectionCb(),
      m_messageCb(),
      m_closeCb(),
      m_writeComplateCb(),
      m_highWaterMarkCb(),
      m_readBuf(),
      m_writeBuf(),
      m_state(State::eConnecting),
      m_connName()
{
    m_channel.setReadCallback(std::bind(&TcpConnection::handleRead, this));
    m_channel.setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
    m_channel.setCloseCallback(std::bind(&TcpConnection::handleClose, this));
}

TcpConnection::~TcpConnection() {
    LOG_INFO << "TcpConnection::~TcpConnection()";
}

void TcpConnection::connectionEstablished() {
    assert(m_state == State::eConnecting);
    setState(State::eConnected);
    m_channel.tie(shared_from_this());
    m_channel.enbaleRead();
    m_connectionCb(shared_from_this());
}


void TcpConnection::send(const std::string& msg) {
    if(m_state == State::eConnected) {
        if (m_loop->isInLoopThread()) {
            sendInLoop(msg);
        } else {
            m_loop->runInLoop(std::bind(&TcpConnection::sendInLoop, this, msg));
        }
    }
}

void TcpConnection::shutdown() {
    if (m_state == State::eConnected) {
        setState(State::eDisconnecting);
        m_loop->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

void TcpConnection::connectionDestroy() {
    m_loop->assertInLoopThread();
    setState(State::eDisconnected);
    m_connectionCb(shared_from_this());
    m_channel.disableAll();
    m_loop->removeChannel(m_channel);
}

void TcpConnection::handleClose() {
    m_loop->assertInLoopThread();
    assert(m_state == State::eConnected || m_state == State::eDisconnecting);
    // setState(State::eDisconnecting);
    m_channel.disableAll();
    m_closeCb(shared_from_this());
}

void TcpConnection::handleError() {
    m_loop->assertInLoopThread();
    CurrectThread::saveErrno(errno);
    LOG_INFO << "TcpConnection::handleError";
    CurrectThread::saveErrno(0);
}

void TcpConnection::handleRead() {
    m_loop->assertInLoopThread();
    ssize_t nread = m_sock.read(m_readBuf);
    if (nread > 0) {
        // read
        m_messageCb(shared_from_this(), &m_readBuf, Timestamp::now());
    } else if (nread == 0) {
        // close
        handleClose();
    } else {
        // 出错
        handleError();
    }
}

void TcpConnection::handleWrite() {
    m_loop->assertInLoopThread();
    if(m_channel.isWriting()) {
        ssize_t nwrite = m_sock.write(m_writeBuf);
        if (nwrite < 0) {
            // 写出错，直接关闭
            LOG_ERROR << "TcpConnection::handleWrite close connection";
            handleClose();
        }
        if (m_writeBuf.size() == 0) {
            m_channel.disableWrite();
            if (m_writeComplateCb) {
                m_loop->runInLoop(std::bind(m_writeComplateCb, shared_from_this()));
            }
            if (m_state == State::eDisconnecting)
                shutdownInLoop();
        }
    }
}

void TcpConnection::sendInLoop(const std::string& msg) {
    m_loop->assertInLoopThread();
    ssize_t nwrite = 0;
    if (!m_channel.isWriting() && m_writeBuf.size() == 0 ) {
        // 没在写，而且buf中没有数据
        nwrite = m_sock.write(msg);
        if(nwrite == static_cast<ssize_t>(msg.size())) {
            if (m_writeComplateCb) {
                m_loop->runInLoop(std::bind(m_writeComplateCb, shared_from_this()));
            }
        }
        if (nwrite < 0) {
            // 直接关闭连接
            LOG_ERROR << "TcpConnection::sendInLoop close connection";
            handleClose();
            return ;
        }
    }
    assert(nwrite >= 0);
    if (static_cast<std::size_t>(nwrite) < msg.size()) {
        m_writeBuf.append(msg.c_str() + nwrite, msg.size() - nwrite);
        if (!m_channel.isWriting()) {
            m_channel.enableWrite();
        }
    }
}

void TcpConnection::shutdownInLoop() {
    m_loop->assertInLoopThread();
    if (!m_channel.isWriting()) {
        m_sock.shutdownWrite();
    }
}






} // namespace net

} // namespace toym


